home *** CD-ROM | disk | FTP | other *** search
/ ADA Programming Guide / ADA Programming Guide.iso / ada_gnu / adainc / a-direio.adb < prev    next >
Text File  |  1996-01-30  |  19KB  |  582 lines

  1. ------------------------------------------------------------------------------
  2. --                                                                          --
  3. --                         GNAT RUNTIME COMPONENTS                          --
  4. --                                                                          --
  5. --                        A D A . D I R E C T _ I O                         --
  6. --                                                                          --
  7. --                                 B o d y                                  --
  8. --                                                                          --
  9. --                            $Revision: 1.5 $                             --
  10. --                                                                          --
  11. --           Copyright (c) 1992,1993,1994 NYU, All Rights Reserved          --
  12. --                                                                          --
  13. -- The GNAT library is free software; you can redistribute it and/or modify --
  14. -- it under terms of the GNU Library General Public License as published by --
  15. -- the Free Software  Foundation; either version 2, or (at your option) any --
  16. -- later version.  The GNAT library is distributed in the hope that it will --
  17. -- be useful, but WITHOUT ANY WARRANTY;  without even  the implied warranty --
  18. -- of MERCHANTABILITY  or  FITNESS FOR  A PARTICULAR PURPOSE.  See the  GNU --
  19. -- Library  General  Public  License for  more  details.  You  should  have --
  20. -- received  a copy of the GNU  Library  General Public License  along with --
  21. -- the GNAT library;  see the file  COPYING.LIB.  If not, write to the Free --
  22. -- Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.        --
  23. --                                                                          --
  24. ------------------------------------------------------------------------------
  25.  
  26. with Ada.Storage_IO;
  27. with Interfaces.C;          use Interfaces.C;
  28. with Interfaces.C.Strings;  use Interfaces.C.Strings;
  29. with System.File_Aux;       use System.File_Aux;
  30.  
  31. package body Ada.Direct_IO is
  32.  
  33.    package Stor_IO is new Ada.Storage_IO (Element_Type => Element_Type);
  34.  
  35.    type Pstring is access String;
  36.  
  37.    type File_Control_Block is record
  38.       Name       : chars_ptr := Null_Ptr;
  39.       Mode       : File_Mode;
  40.       Form       : Pstring;
  41.       Descriptor : C_File_Ptr;
  42.       Index      : Positive_Count;
  43.       Size       : Count;
  44.    end record;
  45.  
  46.    type Open_Type is (Create, Open);
  47.  
  48.    type C_Mode_Type is array (Open_Type, File_Mode) of chars_ptr;
  49.  
  50.    C_Mode : C_Mode_Type := (others => (others => Null_Ptr));
  51.  
  52.    Buffer : Stor_IO.Buffer_Type;
  53.  
  54.    -----------------------
  55.    -- Local Subprograms --
  56.    -----------------------
  57.  
  58.    function To_Element_Index (Index : in C_Long_Int) return Positive_Count;
  59.    pragma Inline (To_Element_Index);
  60.    --  Converts from the zero-based byte index which is used by the C file
  61.    --  positioning functions to the one-based element index which is used
  62.    --  by the Ada.Direct_IO routines.
  63.  
  64.    function To_Byte_Index (Index : in Positive_Count) return C_Long_Int;
  65.    pragma Inline (To_Byte_Index);
  66.    --  Converts from the one-based element index which is used by the
  67.    --  Ada.Direct_IO routines to the zero-based byte index which is used
  68.    --  by the C file positioning functions.
  69.  
  70.    procedure Confirm_File_Is_Open (File : in File_Type);
  71.    pragma Inline (Confirm_File_Is_Open);
  72.    --  Checks to make sure the given file is open.
  73.    --  If not, it raises Status_Error.
  74.  
  75.    procedure Confirm_File_Is_Closed (File : in File_Type);
  76.    pragma Inline (Confirm_File_Is_Closed);
  77.    --  Checks to make sure the given file is closed.
  78.    --  If not, it raises Status_Error.
  79.  
  80.    function New_Temp_File_Name return chars_ptr;
  81.    --  Returns a name that is a valid file name and that is not the same as
  82.    --  the name of an existing external file.
  83.  
  84.    function Current_Size_Of (File : in File_Type) return Count;
  85.    --  Returns the current size in elements of the external file that is
  86.    --  associated with the given file.  The given file must be open.
  87.  
  88.    -----------
  89.    -- Close --
  90.    -----------
  91.  
  92.    procedure Close  (File : in out File_Type) is
  93.    begin
  94.       Confirm_File_Is_Open (File);
  95.  
  96.       if C_Fclose (File.Descriptor) /= 0 then
  97.          raise Device_Error;
  98.       end if;
  99.  
  100.       File := null;
  101.    end Close;
  102.  
  103.    --------------------------
  104.    -- Confirm_File_Is_Open --
  105.    --------------------------
  106.  
  107.    procedure Confirm_File_Is_Open (File : in File_Type) is
  108.    begin
  109.       if not Is_Open (File) then
  110.          raise Status_Error;
  111.       end if;
  112.    end Confirm_File_Is_Open;
  113.  
  114.    ----------------------------
  115.    -- Confirm_File_Is_Closed --
  116.    ----------------------------
  117.  
  118.    procedure Confirm_File_Is_Closed (File : in File_Type) is
  119.    begin
  120.       if Is_Open (File) then
  121.          raise Status_Error;
  122.       end if;
  123.    end Confirm_File_Is_Closed;
  124.  
  125.    ------------
  126.    -- Create --
  127.    ------------
  128.  
  129.    procedure Create
  130.      (File : in out File_Type;
  131.       Mode : in File_Mode := Inout_File;
  132.       Name : in String := "";
  133.       Form : in String := "")
  134.    is
  135.    begin
  136.       Confirm_File_Is_Closed (File);
  137.       File := new File_Control_Block;
  138.  
  139.       --  A null string for Name specifies creation of a temporary file.
  140.  
  141.       if Name'Length = 0 then
  142.          File.Name := New_Temp_File_Name;
  143.       else
  144.          File.Name := New_String (Name);
  145.       end if;
  146.  
  147.       File.Descriptor := C_Fopen (Filename => File.Name,
  148.                                   Mode     => C_Mode (Create, Mode));
  149.  
  150.       --  If the C fopen call fails, it returns a null pointer.
  151.  
  152.       if C_Void_Ptr (File.Descriptor) = C_Null then
  153.          raise Name_Error;
  154.       end if;
  155.  
  156.       File.Mode  := Mode;
  157.       File.Form  := new String'(Form);
  158.  
  159.       --  The size of the external file is needed to implement the Size
  160.       --  function and the End_Of_File function.  The size of the external
  161.       --  file can be found by performing an fseek to the end of the external
  162.       --  file, querying the file position, and then performing another fseek
  163.       --  back to the original position.  This is very portable and reasonably
  164.       --  efficient if done only once.  However, it would be too clumsy to
  165.       --  perform two fseeks every time Size or End_Of_File is called.
  166.       --  Instead, Current_Size_Of (which actually performs the fseeks) is
  167.       --  called only once at the time of the opening of the file.  The size
  168.       --  of the external file is then stored in the file control block.  The
  169.       --  Write procedure is the only procedure that can change the size of
  170.       --  the external file, and it contains code to adjust the size stored
  171.       --  in the file control block if the size of the external file increases.
  172.  
  173.       File.Size  := Current_Size_Of (File);
  174.       File.Index := 1;
  175.    end Create;
  176.  
  177.    ---------------------
  178.    -- Current_Size_Of --
  179.    ---------------------
  180.  
  181.    function Current_Size_Of (File : in File_Type) return Count is
  182.       Current_Byte_Index : C_Long_Int;
  183.       Current_Byte_Size  : C_Long_Int;
  184.  
  185.    begin
  186.       Current_Byte_Index := C_Ftell (File.Descriptor);
  187.  
  188.       if C_Fseek (Stream => File.Descriptor,
  189.                   Offset => 0,
  190.                   Whence => C_Seek_End) /= 0 then
  191.          raise Device_Error;
  192.       end if;
  193.  
  194.       Current_Byte_Size := C_Ftell (File.Descriptor);
  195.  
  196.       if C_Fseek (Stream => File.Descriptor,
  197.                   Offset => Current_Byte_Index,
  198.                   Whence => C_Seek_Set) /= 0 then
  199.          raise Device_Error;
  200.       end if;
  201.  
  202.       return To_Element_Index (Current_Byte_Size) - 1;
  203.    end Current_Size_Of;
  204.  
  205.    ------------
  206.    -- Delete --
  207.    ------------
  208.  
  209.    procedure Delete (File : in out File_Type) is
  210.       File_Name_To_Delete : chars_ptr;
  211.  
  212.    begin
  213.       Confirm_File_Is_Open (File);
  214.  
  215.       --  The file should be closed before calling the C remove function.
  216.       --  If the file is open, the behavior of the remove function is
  217.       --  implementation-defined.  Closing the file, however, means we
  218.       --  lose the info in the file control block, so we have to save the
  219.       --  file name temporarily in order to have it for use with the remove
  220.       --  function.
  221.  
  222.       File_Name_To_Delete := File.Name;
  223.       Close (File);
  224.  
  225.       if C_Remove (File_Name_To_Delete) /= 0 then
  226.          raise Use_Error;
  227.       end if;
  228.    end Delete;
  229.  
  230.    ----------
  231.    -- Form --
  232.    ----------
  233.  
  234.    function Form (File : in File_Type) return String is
  235.    begin
  236.       Confirm_File_Is_Open (File);
  237.       return File.Form.all;
  238.    end Form;
  239.  
  240.    -----------
  241.    -- Index --
  242.    -----------
  243.  
  244.    function Index (File : in File_Type) return Positive_Count is
  245.    begin
  246.       Confirm_File_Is_Open (File);
  247.       return File.Index;
  248.    end Index;
  249.  
  250.    -------------
  251.    -- Is_Open --
  252.    -------------
  253.  
  254.    function Is_Open (File : in File_Type) return Boolean is
  255.    begin
  256.       return File /= null;
  257.    end Is_Open;
  258.  
  259.    ----------
  260.    -- Mode --
  261.    ----------
  262.  
  263.    function Mode (File : in File_Type) return File_Mode is
  264.    begin
  265.       Confirm_File_Is_Open (File);
  266.       return File.Mode;
  267.    end Mode;
  268.  
  269.    ----------
  270.    -- Name --
  271.    ----------
  272.  
  273.    function Name (File : in File_Type) return String is
  274.    begin
  275.       Confirm_File_Is_Open (File);
  276.       return Value (File.Name);
  277.    end Name;
  278.  
  279.    ------------------------
  280.    -- New_Temp_File_Name --
  281.    ------------------------
  282.  
  283.    function New_Temp_File_Name return chars_ptr is
  284.       Temp_File_Name   : String := "ADATMPXX";
  285.       C_Temp_File_Name : chars_ptr;
  286.  
  287.    begin
  288.       C_Temp_File_Name := New_String (Temp_File_Name);
  289.       C_Temp_File_Name := C_Mktemp (C_Temp_File_Name);
  290.       return C_Temp_File_Name;
  291.    end New_Temp_File_Name;
  292.  
  293.    ----------
  294.    -- Open --
  295.    ----------
  296.  
  297.    procedure Open
  298.      (File : in out File_Type;
  299.       Mode : in File_Mode;
  300.       Name : in String;
  301.       Form : in String := "")
  302.    is
  303.    begin
  304.       Confirm_File_Is_Closed (File);
  305.       File := new File_Control_Block;
  306.  
  307.       File.Name := New_String (Name);
  308.       File.Descriptor := C_Fopen (Filename => File.Name,
  309.                                   Mode     => C_Mode (Open, Mode));
  310.  
  311.       --  If the C fopen call fails, it returns a null pointer.
  312.  
  313.       if C_Void_Ptr (File.Descriptor) = C_Null then
  314.          raise Name_Error;
  315.       end if;
  316.  
  317.       File.Mode  := Mode;
  318.       File.Form  := new String'(Form);
  319.  
  320.       --  The size of the external file is needed to implement the Size
  321.       --  function and the End_Of_File function.  The size of the external
  322.       --  file can be found by performing an fseek to the end of the external
  323.       --  file, querying the file position, and then performing another fseek
  324.       --  back to the original position.  This is very portable and reasonably
  325.       --  efficient if done only once.  However, it would be too clumsy to
  326.       --  perform two fseeks every time Size or End_Of_File is called.
  327.       --  Instead, Current_Size_Of (which actually performs the fseeks) is
  328.       --  called only once at the time of the opening of the file.  The size
  329.       --  of the external file is then stored in the file control block.  The
  330.       --  Write procedure is the only procedure that can change the size of
  331.       --  the external file, and it contains code to adjust the size stored
  332.       --  in the file control block if the size of the external file increases.
  333.  
  334.       File.Size  := Current_Size_Of (File);
  335.       File.Index := 1;
  336.    end Open;
  337.  
  338.    ----------
  339.    -- Read --
  340.    ----------
  341.  
  342.    procedure Read
  343.      (File : in  File_Type;
  344.       Item : out Element_Type;
  345.       From : in  Positive_Count)
  346.    is
  347.    begin
  348.       Confirm_File_Is_Open (File);
  349.       Set_Index (File, From);
  350.       Read (File, Item);
  351.    end Read;
  352.  
  353.    procedure Read (File : in File_Type; Item : out Element_Type) is
  354.    begin
  355.       Confirm_File_Is_Open (File);
  356.  
  357.       if File.Mode = Out_File then
  358.          raise Mode_Error;
  359.       end if;
  360.  
  361.       if End_Of_File (File) then
  362.          raise End_Error;
  363.       end if;
  364.  
  365.       --  Peforming an fseek here forces the current index stored in the
  366.       --  file control block to match the file position indicator used by
  367.       --  the C file IO functions.  They might not match due to a previous
  368.       --  call to Set_Index.  Additionally, this takes care of the buffering
  369.       --  problem associated with update mode files.  Such files may not mix
  370.       --  reads and writes without an intervening call to fflush or to a
  371.       --  file positioning function (fseek, fsetpos, or rewind).
  372.  
  373.       if C_Fseek (Stream => File.Descriptor,
  374.                   Offset => To_Byte_Index (File.Index),
  375.                   Whence => C_Seek_Set) /= 0
  376.       then
  377.          raise Device_Error;
  378.       end if;
  379.  
  380.       --  The C fread function returns the number of elements successfully
  381.       --  read.  Since we only read one element at a time and we have already
  382.       --  checked for end of file, if the number of elements successfully read
  383.       --  does not equal the number of elements requested, it is considered to
  384.       --  be a Device_Error.
  385.  
  386.       if C_Fread (Ptr    => C_Void_Ptr (Buffer'Address),
  387.                   Size   => C_Size_T (Buffer'Length),
  388.                   Nmemb  => 1,
  389.                   Stream => File.Descriptor) /= 1
  390.       then
  391.          raise Device_Error;
  392.       end if;
  393.  
  394.       Stor_IO.Read (Buffer, Item);
  395.       File.Index := File.Index + 1;
  396.    end Read;
  397.  
  398.    -----------
  399.    -- Reset --
  400.    -----------
  401.  
  402.    procedure Reset  (File : in out File_Type; Mode : in File_Mode) is
  403.       Old_File : File_Type := File;
  404.  
  405.    begin
  406.       Confirm_File_Is_Open (File);
  407.       Close (File);
  408.       Open (File, Mode, Value (Old_File.Name), Old_File.Form.all);
  409.    end Reset;
  410.  
  411.    procedure Reset  (File : in out File_Type) is
  412.    begin
  413.       Confirm_File_Is_Open (File);
  414.       Reset (File, File.Mode);
  415.    end Reset;
  416.  
  417.    ---------------
  418.    -- Set_Index --
  419.    ---------------
  420.  
  421.    procedure Set_Index (File : in File_Type; To : in Positive_Count) is
  422.    begin
  423.       Confirm_File_Is_Open (File);
  424.  
  425.       --  It is not an error to set the current index of the given file to
  426.       --  a value which exceeds the current size of the file.
  427.  
  428.       File.Index := To;
  429.    end Set_Index;
  430.  
  431.    ----------
  432.    -- Size --
  433.    ----------
  434.  
  435.    function Size (File : in File_Type) return Count is
  436.    begin
  437.       Confirm_File_Is_Open (File);
  438.       return File.Size;
  439.    end Size;
  440.  
  441.    ----------------------
  442.    -- To_Element_Index --
  443.    ----------------------
  444.  
  445.    function To_Element_Index (Index : in C_Long_Int) return Positive_Count is
  446.    begin
  447.       return Positive_Count ((Index / Buffer'Length) + 1);
  448.    end To_Element_Index;
  449.  
  450.    -------------------
  451.    -- To_Byte_Index --
  452.    -------------------
  453.  
  454.    function To_Byte_Index (Index : in Positive_Count) return C_Long_Int is
  455.    begin
  456.       return C_Long_Int ((Count (Index) - 1) * Buffer'Length);
  457.    end To_Byte_Index;
  458.  
  459.    -----------
  460.    -- Write --
  461.    -----------
  462.  
  463.    procedure Write
  464.      (File : in File_Type;
  465.       Item : in Element_Type;
  466.       To   : in Positive_Count)
  467.    is
  468.    begin
  469.       Confirm_File_Is_Open (File);
  470.       Set_Index (File, To);
  471.       Write (File, Item);
  472.    end Write;
  473.  
  474.    procedure Write (File : in File_Type; Item : in Element_Type) is
  475.    begin
  476.       Confirm_File_Is_Open (File);
  477.  
  478.       if File.Mode = In_File then
  479.          raise Mode_Error;
  480.       end if;
  481.  
  482.       Stor_IO.Write (Buffer, Item);
  483.  
  484.       --  Peforming an fseek here forces the current index stored in the
  485.       --  file control block to match the file position indicator used by
  486.       --  the C file IO functions.  They might not match due to a previous
  487.       --  call to Set_Index.  Additionally, this takes care of the buffering
  488.       --  problem associated with update mode files.  Such files may not mix
  489.       --  reads and writes without an intervening call to fflush or to a
  490.       --  file positioning function (fseek, fsetpos, or rewind).
  491.  
  492.       if C_Fseek (Stream => File.Descriptor,
  493.                   Offset => To_Byte_Index (File.Index),
  494.                   Whence => C_Seek_Set) /= 0
  495.       then
  496.          raise Device_Error;
  497.       end if;
  498.  
  499.       --  The C fwrite function returns the number of elements successfully
  500.       --  written, which will less than the number of elements requested only
  501.       --  if a write error is encountered.  Such a situation is considered to
  502.       --  be a Device_Error.
  503.  
  504.       if C_Fwrite (Ptr    => C_Void_Ptr (Buffer'Address),
  505.                    Size   => C_Size_T (Buffer'Length),
  506.                    Nmemb  => 1,
  507.                    Stream => File.Descriptor) /= 1
  508.       then
  509.          raise Device_Error;
  510.       end if;
  511.  
  512.       --  If the size of the file has increased, store the new size in the
  513.       --  file control block.
  514.  
  515.       if File.Index > File.Size then
  516.          File.Size := File.Index;
  517.       end if;
  518.       File.Index := File.Index + 1;
  519.    end Write;
  520.  
  521.    -----------------
  522.    -- End_Of_File --
  523.    -----------------
  524.  
  525.    function End_Of_File (File : in File_Type) return Boolean is
  526.    begin
  527.       Confirm_File_Is_Open (File);
  528.  
  529.       if File.Mode = Out_File then
  530.          raise Mode_Error;
  531.       end if;
  532.  
  533.       return Index (File) > Size (File);
  534.    end End_Of_File;
  535.  
  536. begin
  537.    -------------------------
  538.    -- Package Elaboration --
  539.    -------------------------
  540.  
  541.    --  The following possible modes for the C fopen function are given here
  542.    --  for reference:
  543.  
  544.    --  r   open text file for reading
  545.    --  w   truncate to zero length or create text file for writing
  546.    --  a   append; open or create text file for writing at end-of-file
  547.    --  r   open file for reading
  548.    --  w   truncate to zero length or create file for writing
  549.    --  a   append; open or create file for writing at end-of-file
  550.    --  r+  open text file for update (reading and writing)
  551.    --  w+  truncate to zero length or create text file for update
  552.    --  a+  append; open or create text file for update, writing at end-of-file
  553.    --  r+  open file for update (reading and writing)
  554.    --  w+  truncate to zero length or create file for update
  555.    --  a+  append; open or create file for update, writing at end-of-file
  556.  
  557.    --  Notes:
  558.  
  559.    --  (1) Opening a file with read mode fails if the file does not exist or
  560.    --  cannot be read.
  561.  
  562.    --  (2) Opening a file with append mode causes all subsequent writes to the
  563.    --  file to be forced to the then current end-of-file, regardless of
  564.    --  intervening calls to the fseek function.
  565.  
  566.    --  (3) When a file is opened with update mode, both input and output may be
  567.    --  performed on the associated stream.  However, output may not be directly
  568.    --  followed by input without an intervening call to the fflush function or
  569.    --  to a file positioning function (fseek, fsetpos, or rewind), and input
  570.    --  may not be directly followed by output without an intervening call to a
  571.    --  file positioning function, unless the input operation encounters
  572.    --  end-of-file.
  573.  
  574.    C_Mode (Create, In_File)    := New_String ("w+");
  575.    C_Mode (Create, Out_File)   := New_String ("w+");
  576.    C_Mode (Create, Inout_File) := New_String ("w+");
  577.  
  578.    C_Mode (Open,   In_File)    := New_String ("r+");
  579.    C_Mode (Open,   Out_File)   := New_String ("r+");
  580.    C_Mode (Open,   Inout_File) := New_String ("r+");
  581. end Ada.Direct_IO;
  582.